package bll

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"gitlab.local/backend/proto/dto"
	"gitlab.local/backend/proto/manager"
	"go.uber.org/atomic"
	"gorm.io/gorm"
	"ota/auth"
	"ota/client/grpc"
	log "ota/collector/logger"
	"ota/errors"
	"ota/event"
	"ota/model"
	"ota/model/entity"
	"ota/model/mapping"
	"ota/store"
	"ota/store/postgres"
	"ota/utils"
	"sync"
	"time"
)

type upgradePlan struct {
	iUpgradePlan     store.IUpgradePlan
	iUpgradeStrategy store.IUpgradeStrategy
	iUserUpgrade     store.IUserUpgrade
	*userUpgrade
}

var UpgradePlan = &upgradePlan{
	iUpgradePlan:     postgres.UpgradePlan,
	iUpgradeStrategy: postgres.UpgradeStrategy,
	iUserUpgrade:     postgres.UserUpgrade,
	userUpgrade:      UserUpgrade,
}

func init() {
	Register(UpgradePlan)
}

func (a *upgradePlan) init() func() {
	return func() {}
}

func (a *upgradePlan) onEvent(*event.Data) {}

func (a *upgradePlan) checkNameExist(ctx context.Context, name string) error {

	// 车型名称校验
	var count int64
	postgres.GetDB(ctx).Model(&entity.UpgradePlan{}).Where("name = ?", name).Count(&count)
	if count > 0 {
		return fmt.Errorf("升级计划名称已存在")
	}

	return nil
}

func (a *upgradePlan) check(ctx context.Context, id int64, op model.OtaUpgradePlanOpType, f func(plan *entity.UpgradePlan)) error {

	var (
		allowStatus = map[model.OtaUpgradePlanOpType]map[model.OtaUpgradePlanStatusType]error{
			model.UpgradePlanOpUpdate: {
				model.UpgradePlanStatusPending:   nil,
				model.UpgradePlanStatusUpgrading: fmt.Errorf("任务升级中,禁止修改"),
				model.UpgradePlanStatusCompleted: fmt.Errorf("任务已完成,禁止修改"),
				model.UpgradePlanStatusCanceled:  fmt.Errorf("任务已被终止,禁止修改"),
			},
			model.UpgradePlanOpDelete: {
				model.UpgradePlanStatusPending:   nil,
				model.UpgradePlanStatusUpgrading: fmt.Errorf("任务执行中,不可删除"),
				model.UpgradePlanStatusCompleted: nil,
				model.UpgradePlanStatusCanceled:  nil,
			},
			model.UpgradePlanOpCanceled: {
				model.UpgradePlanStatusPending:   nil,
				model.UpgradePlanStatusUpgrading: nil,
				model.UpgradePlanStatusCompleted: fmt.Errorf("任务已完成,不可终止"),
				model.UpgradePlanStatusCanceled:  nil,
			},
		}
	)
	respUpgradePlan, err := a.iUpgradePlan.Find(ctx, &model.UpgradePlanInfoRequest{Id: id})
	if err != nil {
		log.Errorf("get upgrade plan err: %v", err)
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.InternalServerError.Error()
		}
	}
	if respUpgradePlan.Id > 0 {
		if f != nil {
			f(respUpgradePlan)
		}
		return allowStatus[op][model.OtaUpgradePlanStatusType(respUpgradePlan.Status)]
	}

	return errors.UpgradePlanNotExist.Error()
}

func (a *upgradePlan) getQueryCondAndDetail(ctx context.Context, planId int64) (json.RawMessage, *model.UpgradePlanInfo, *model.OtaUpgradePlanQueryCond, error) {

	var (
		err             error
		jsonDetail      json.RawMessage
		respUpgradePlan *model.UpgradePlanInfo
	)
	respUpgradePlan, err = UpgradePlan.Find(ctx, &model.UpgradePlanInfoRequest{Id: planId})
	if err != nil {
		return jsonDetail, respUpgradePlan, nil, err
	} else {
		jsonDetail = respUpgradePlan.Detail
	}

	qc := &model.OtaUpgradePlanQueryCond{}
	if model.TargetType(respUpgradePlan.TargetType) != model.TargetTypeAll {
		if e := json.Unmarshal(respUpgradePlan.QueryCond, qc); e != nil {
			return jsonDetail, respUpgradePlan, nil, e
		}
	}
	return jsonDetail, respUpgradePlan, qc, nil
}

func (a *upgradePlan) spliceEBikeListReq(targetType model.TargetType, qc *model.OtaUpgradePlanQueryCond) []*manager.GetEbikeListReq {

	var (
		defaultSize = 200
		reqs        []*manager.GetEbikeListReq
	)
	switch targetType {
	case model.TargetTypeEBikeIds:
		for i, v := range utils.SplitSlice(qc.EbikeId, defaultSize) {
			reqs = append(reqs, &manager.GetEbikeListReq{
				BikeIds:  v,
				Index:    int32(i + 1),
				OnlyList: utils.GetValuePointer(true),
				Size:     int32(len(v)),
			})
		}
		break
	case model.TargetTypeEBikeTypes:
		for i, v := range utils.SplitSlice(qc.EBikeType, defaultSize) {
			reqs = append(reqs, &manager.GetEbikeListReq{
				BikeTypeIds: v,
				Index:       int32(i + 1),
				OnlyList:    utils.GetValuePointer(true),
				Size:        int32(len(v)),
			})
		}
		break
	case model.TargetTypeUsers:
		for i, v := range utils.SplitSlice(qc.Users, defaultSize) {
			reqs = append(reqs, &manager.GetEbikeListReq{
				UserIds:  v,
				Index:    int32(i + 1),
				OnlyList: utils.GetValuePointer(true),
				Size:     int32(len(v)),
			})
		}
		break
	default:
		panic("unhandled default case")
	}

	return reqs
}

func (a *upgradePlan) getUpgradeDevices(ctx context.Context, in *model.GetUpgradeDevicesRequest) (json.RawMessage, *model.UpgradePlanInfo, []*model.UserUpgradeCreateRequest, error) {

	var (
		err             error
		jsonDetail      json.RawMessage
		respUpgradePlan *model.UpgradePlanInfo
		userUpgradeData []*model.UserUpgradeCreateRequest
		qc              *model.OtaUpgradePlanQueryCond
	)

	jsonDetail, respUpgradePlan, qc, err = a.getQueryCondAndDetail(ctx, in.PlanId)
	if err != nil {
		return jsonDetail, nil, nil, err
	}

	var (
		eBikeIdList []int64
		reqs        = a.spliceEBikeListReq(model.TargetType(respUpgradePlan.TargetType), qc)
	)

	ctx, _ = context.WithTimeout(ctx, time.Minute*5)

	for _, req := range reqs {

		var (
			userIds      []int64
			userId2Index = map[int64][]int{}
		)
		bikes, e := grpc.EBikeCmd.GetEbikeList(ctx, req)
		if e != nil {
			return jsonDetail, nil, nil, e
		}

		uData := make([]*model.UserUpgradeCreateRequest, len(bikes.List), len(bikes.List))
		for i, v := range bikes.List {
			if v.UserId > 0 {
				userIds = append(userIds, v.UserId)
				userId2Index[v.UserId] = append(userId2Index[v.UserId], i)
			}

			uData[i] = &model.UserUpgradeCreateRequest{
				UserId:        v.UserId,
				PlanId:        respUpgradePlan.Id,
				PlanName:      respUpgradePlan.Name,
				StrategyId:    respUpgradePlan.StrategyId,
				UpgradeType:   respUpgradePlan.UpgradeType,
				EbikeId:       v.BikeId,
				EbikeName:     v.Name,
				EbikeSnCode:   v.SnCode,
				EbikeTypeId:   v.BikeTypeId,
				EbikeTypeName: v.BikeTypeName,
				Detail:        jsonDetail,
				Status:        int(model.UpgradeResultPending),
				/* TODO */
				StatusDetail: model.UserUpgradeStatusDetail{}.Json(),
			}
			eBikeIdList = append(eBikeIdList, v.BikeId)
		}

		if respUsers, e := grpc.UCmd.UserListInfo(ctx, &dto.UserListInfoRequest{Ids: userIds}); e != nil {
			return jsonDetail, respUpgradePlan, nil, e
		} else {
			for _, v := range respUsers.List {
				for _, index := range userId2Index[v.Id] {
					uData[index].UserName = v.Name
					uData[index].UserPhone = v.Phone
				}
			}
		}
		userUpgradeData = append(userUpgradeData, uData...)
	}

	var (
		respUserUpgrade    *model.UserUpgradeListResponse
		respUserUpgradeMap = map[int64]struct{}{}
		selectFields       = []string{"ebike_id", "max(id) as id"}
		groupBy            = []string{"ebike_id"}
		splitEBikeIdList   = utils.SplitSlice(eBikeIdList, 200)
	)
	/*
	     select
	   			ebike_id,max(id) as max_id
	   		from
	   			user_upgrade
	   		where status = 1 and ebike_id in (1,2,3,4,5)
	   		group by ebike_id
	*/

	/*
	        如果添加任务的时候，已经有正在执行的任务，那么将添加的任务的状态置为 (待执行)status = OtaUpgradeResultPending && (等待执行)upgrading_status = OtaUpgradeResultUpgradingQueueing
	   	   	并且不会执行，执行时机是正在执行的任务执行完成，调用 UpgradeNext 函数执行
	*/

	for _, v := range splitEBikeIdList {
		if respUserUpgrade, err = UserUpgrade.List(ctx, &model.UserUpgradeListRequest{
			Status:   utils.GetValuePointer(int(model.UpgradeResultUpgrading)),
			EbikeIds: v,
			Index:    1,
			Size:     -1,
		}, model.QueryOptions{SelectFields: selectFields, GroupBy: groupBy, OnlyList: true}); err != nil {
			return jsonDetail, nil, nil, err
		} else {
			for _, vv := range respUserUpgrade.List {
				respUserUpgradeMap[vv.EbikeId] = struct{}{}
			}
		}
	}

	var (
		onlineTotal  int
		offlineTotal int
		queueTotal   int
	)
	for i, vvv := range userUpgradeData {

		if !IsOnline(vvv.EbikeSnCode) {
			userUpgradeData[i].UpgradingStatus = int(model.OtaUpgradeResultUpgradingOfflineDelaying)
			offlineTotal += 1
		} else {
			onlineTotal += 1
		}

		if _, ok := respUserUpgradeMap[vvv.EbikeId]; ok {
			userUpgradeData[i].UpgradingStatus = int(model.OtaUpgradeResultUpgradingQueueing)
			queueTotal += 1
		}
	}

	log.Infof("load device statistics device(total:%v, online:%v offline:%v queueing:%v)", len(userUpgradeData), onlineTotal, offlineTotal, queueTotal)

	return jsonDetail, respUpgradePlan, userUpgradeData, nil
}

// create upgrade plan batch
//func (a *upgradePlan) createUserUpgradeBatch(ctx context.Context, id int64, fCallBack func([]*model.OtaUpgradeRequest) error) error {
//
//	var (
//		err             error
//		jsonDetail      json.RawMessage
//		respUpgradePlan *model.UpgradePlanInfo
//		userUpgradeData []*model.UserUpgradeCreateRequest
//	)
//
//	ctx, _ = context.WithTimeout(ctx, time.Minute*5)
//	if jsonDetail, respUpgradePlan, userUpgradeData, err = a.getUpgradeDevices(ctx, &model.GetUpgradeDevicesRequest{PlanId: id}); err != nil {
//		return err
//	}
//
//	if len(userUpgradeData) == 0 {
//		return nil
//	}
//
//	err = UserUpgrade.CreateInBatch(ctx, userUpgradeData, 200)
//	if err != nil {
//		return err
//	}
//
//	var (
//		upgradeRequests []*model.OtaUpgradeRequest
//	)
//	for i, v := range userUpgradeData {
//		if model.OtaUpgradeResultUpgradingType(v.UpgradingStatus) != model.OtaUpgradeResultUpgradingNormal {
//			continue
//		}
//		/*
//		   触发升级的任务是'升级中' 的任务并且 升级中的状态 是'正常升级中'
//		   status = model.UpgradeResultUpgrading && upgrading_status == model.OtaUpgradeResultUpgradingNormal
//		*/
//		upgradeRequests = append(upgradeRequests, &model.OtaUpgradeRequest{
//			ID:         userUpgradeData[i].Id,
//			SNCode:     userUpgradeData[i].EbikeSnCode,
//			Status:     utils.GetValuePointer(model.OtaAgree),
//			PlanId:     respUpgradePlan.Id,
//			StrategyId: respUpgradePlan.StrategyId,
//		})
//	}
//
//	upgradePlanStatus := model.UpgradePlanStatusUpgrading
//	if len(upgradeRequests) == 0 {
//		return nil
//	}
//
//	if err = UpgradePlan.Update(ctx, &model.UpgradePlanUpdateRequest{
//		Id:     &respUpgradePlan.Id,
//		Detail: jsonDetail,
//		Status: utils.GetValuePointer(int(upgradePlanStatus)),
//	}); err != nil {
//		return err
//	}
//
//	if v, e := UpgradeStrategy.Find(ctx, &model.UpgradeStrategyInfoRequest{Id: respUpgradePlan.StrategyId}); e != nil {
//		return e
//	} else {
//		if v.Id == 0 {
//			return errors.UpgradeStrategyNotExist.Error()
//		}
//		if model.UpgradeType(v.UpgradeType) == model.OtaSilence && len(upgradeRequests) > 0 {
//			return fCallBack(upgradeRequests)
//		}
//	}
//
//	return nil
//}

func (a *upgradePlan) resetUserUpgradeBatch(ctx context.Context, id int64) error {

	var (
		err             error
		userUpgradeData []*model.UserUpgradeCreateRequest
	)

	ctx, _ = context.WithTimeout(ctx, time.Minute*5)
	if _, _, userUpgradeData, err = a.getUpgradeDevices(ctx, &model.GetUpgradeDevicesRequest{PlanId: id}); err != nil {
		return err
	}

	if len(userUpgradeData) == 0 {
		return nil
	}

	err = UserUpgrade.CreateInBatch(ctx, userUpgradeData, 200)
	if err != nil {
		return err
	}

	return nil
}

func (a *upgradePlan) execOtaSilenceUpgrade(ctx context.Context, id int64, fCallBack func([]*model.OtaUpgradeRequest) error) error {

	var (
		err             error
		jsonDetail      json.RawMessage
		respUpgradePlan *model.UpgradePlanInfo
	)

	jsonDetail, respUpgradePlan, _, err = a.getQueryCondAndDetail(ctx, id)
	if err != nil {
		return err
	}

	var (
		index           = 1
		size            = 500
		upgradeRequests []*model.OtaUpgradeRequest
	)
	for {
		_, list, err := a.iUserUpgrade.List(ctx, &model.UserUpgradeListRequest{
			PlanId:          utils.GetValuePointer(id),
			UpgradingStatus: utils.GetValuePointer(int(model.OtaUpgradeResultUpgradingNormal)),
			Index:           index,
			Size:            size,
		}, model.GetQueryOption(model.QueryOptions{OnlyList: true}))
		if err != nil {
			return errors.Wrap(err, "list user upgrade fail")
		}

		for _, l := range list {
			if model.OtaUpgradeResultUpgradingType(l.UpgradingStatus) != model.OtaUpgradeResultUpgradingNormal {
				continue
			}

			/*
			   触发升级的任务是'升级中' 的任务并且 升级中的状态 是'正常升级中'
			   status = model.UpgradeResultUpgrading && upgrading_status == model.OtaUpgradeResultUpgradingNormal
			*/

			upgradeRequests = append(upgradeRequests, &model.OtaUpgradeRequest{
				ID:         l.Id,
				SNCode:     l.EbikeSnCode,
				Status:     utils.GetValuePointer(model.OtaAgree),
				PlanId:     l.Id,
				StrategyId: l.StrategyId,
			})
		}

		if len(list) < size {
			break
		}
		index = index + 1
	}

	upgradePlanStatus := model.UpgradePlanStatusUpgrading
	if len(upgradeRequests) == 0 {
		return nil
	}

	if err := UpgradePlan.Update(ctx, &model.UpgradePlanUpdateRequest{
		Id:     utils.GetValuePointer(respUpgradePlan.Id),
		Detail: jsonDetail,
		Status: utils.GetValuePointer(int(upgradePlanStatus)),
	}); err != nil {
		return err
	}

	if v, e := UpgradeStrategy.Find(ctx, &model.UpgradeStrategyInfoRequest{Id: respUpgradePlan.StrategyId}); e != nil {
		return e
	} else {
		if v.Id == 0 {
			return errors.UpgradeStrategyNotExist.Error()
		}
		if model.UpgradeType(v.UpgradeType) == model.OtaSilence && len(upgradeRequests) > 0 {
			return fCallBack(upgradeRequests)
		}
	}

	return nil
}

func (a *upgradePlan) checkQueryCond(ctx context.Context, in *model.CheckQueryCondType) error {

	var (
		userUpgradeReqs []*model.UserUpgradeListRequest
		upgradePlanReqs []*model.UpgradePlanListRequest
		option          = model.GetQueryOption(model.QueryOptions{OnlyList: true})
		defaultSize     = 2000
		wp              = sync.WaitGroup{}
	)

	qc := &model.OtaUpgradePlanQueryCond{}
	if model.TargetType(in.TargetType) != model.TargetTypeAll {
		if e := json.Unmarshal(in.QueryCond, qc); e != nil {
			return errors.InternalServerError.Error()
		}
	}

	switch model.TargetType(in.TargetType) {
	case model.TargetTypeEBikeIds:
		for i, v := range utils.SplitSlice(qc.EbikeId, defaultSize) {
			userUpgradeReqs = append(userUpgradeReqs, &model.UserUpgradeListRequest{
				Statuses: []int{int(model.UpgradeResultPending), int(model.UpgradeResultUpgrading)},
				EbikeIds: v,
				Index:    i + 1,
				Size:     len(v),
			})
			upgradePlanReqs = append(upgradePlanReqs, &model.UpgradePlanListRequest{
				Status: utils.GetValuePointer(int(model.UpgradePlanStatusPending)),
				Index:  i + 1,
				Size:   len(v),
			})
		}
		break
	case model.TargetTypeEBikeTypes:
		for i, v := range utils.SplitSlice(qc.EbikeId, defaultSize) {
			userUpgradeReqs = append(userUpgradeReqs, &model.UserUpgradeListRequest{
				Statuses:     []int{int(model.UpgradeResultPending), int(model.UpgradeResultUpgrading)},
				EbikeTypeIds: v,
				Index:        i + 1,
				Size:         len(v),
			})
		}
		break
	case model.TargetTypeUsers:
		for i, v := range utils.SplitSlice(qc.EbikeId, defaultSize) {
			userUpgradeReqs = append(userUpgradeReqs, &model.UserUpgradeListRequest{
				Statuses:     []int{int(model.UpgradeResultPending), int(model.UpgradeResultUpgrading)},
				EbikeTypeIds: v,
				Index:        i + 1,
				Size:         len(v),
			})
		}
		break
	default:
		return fmt.Errorf("nusupport target type")
	}

	var (
		err  atomic.Error
		sMap = map[int]string{
			1: "待执行",
			2: "执行中",
		}
	)
	wp.Add(len(userUpgradeReqs))
	for i := 0; i < len(userUpgradeReqs); i++ {
		go func(index int) {

			defer wp.Done()
			if _, list, e := a.iUserUpgrade.List(ctx, userUpgradeReqs[index], option); e != nil {
				err.Store(errors.InternalServerError.Error())
			} else {
				if len(list) > 0 {
					err.Store(fmt.Errorf("提交失败:设备%v已有正在运行的任务，状态是%v", list[0].EbikeSnCode, sMap[list[0].Status]))
					ctx.Done()
				}
			}
		}(i)
	}
	wp.Wait()

	return err.Load()
}

// Create
func (a *upgradePlan) Create(ctx context.Context, in *model.UpgradePlanCreateRequest) error {

	//if err := a.checkNameExist(ctx, in.Name); err != nil {
	//	return err
	//}

	var (
		err error
		t   = time.Now().Unix()
	)

	if in.StartTime <= t {
		return fmt.Errorf("开始时间必须大于当前时间")
	}

	if err := a.checkQueryCond(ctx, &model.CheckQueryCondType{TargetType: in.TargetType, QueryCond: in.QueryCond}); err != nil {
		return err
	}

	c := buildUpgradePlan(ctx, in)
	c.Status = int(model.UpgradePlanStatusPending)
	in.Id, err = a.iUpgradePlan.Create(ctx, c)
	if err != nil {
		return err
	}

	event.Emit(&event.Data{Type: model.UpgradePlanOpCreate, Data: in})
	go func() {
		reqCtx, _ := context.WithTimeout(context.Background(), time.Minute*3)
		if err = a.resetUserUpgradeBatch(reqCtx, in.Id); err != nil {
			log.Errorf("初始化 user_upgrade 数据失败:%v plan_id:%v", err, in.Id)
		}
	}()
	return err
}

func (a *upgradePlan) Update(ctx context.Context, in *model.UpgradePlanUpdateRequest) error {

	var (
		respUpgradePlan *entity.UpgradePlan
		target          int
	)

	if err := a.check(ctx, *in.Id, model.UpgradePlanOpUpdate, func(plan *entity.UpgradePlan) {
		respUpgradePlan = plan
		target = respUpgradePlan.TargetType
	}); err != nil {
		return err
	}

	var (
		emit bool
		t    = time.Now().Unix()
		dict = make(map[string]interface{})
	)

	if in.Id != nil {
		dict["id"] = in.Id
	}

	if in.StrategyId != nil {
		dict["strategy_id"] = in.StrategyId
		if respUpgradePlan.StrategyId != *in.StrategyId {
			dict["upgrade_type"], dict["detail"] = buildDetailData(ctx, *in.StrategyId)
		}
	}

	if in.Name != nil {
		dict["name"] = in.Name
	}

	if in.TargetType != nil {
		dict["target_type"] = in.TargetType
		target = *in.TargetType
	}

	if in.StartTime != nil {
		if *in.StartTime <= t {
			return fmt.Errorf("开始时间必须大于当前时间")
		}

		if *in.StartTime != t {
			emit = true
			dict["start_time"] = in.StartTime
		}
	}

	if in.Status != nil {
		dict["status"] = in.Status
	}

	if in.Overseer != nil {
		dict["overseer"] = in.Overseer
	}

	if in.QueryCond != nil {
		if err := a.checkQueryCond(ctx, &model.CheckQueryCondType{TargetType: target, QueryCond: in.QueryCond}); err != nil {
			return err
		}
		if !bytes.EqualFold(in.QueryCond, respUpgradePlan.QueryCond) {
			defer func() {
				go func() {

					reqCtx, _ := context.WithTimeout(context.Background(), time.Minute*3)
					if err := a.iUserUpgrade.DeleteByPlanId(reqCtx, *in.Id); err != nil {
						log.Errorf("初始化 user_upgrade 数据失败(删除失败):%v plan_id:%v", err, in.Id)
					} else {
						if err := a.resetUserUpgradeBatch(reqCtx, *in.Id); err != nil {
							log.Errorf("初始化 user_upgrade 数据失败(更新失败):%v plan_id:%v", err, in.Id)
						}
					}
				}()
			}()
		}

		dict["query_cond"] = in.QueryCond
	}

	if in.Tester != nil {
		dict["tester"] = in.Tester
	}

	if in.Inspector != nil {
		dict["inspector"] = in.Inspector
	}

	if in.CreatedAt != nil {
		dict["created_at"] = in.CreatedAt
	}

	dict["updated_at"] = time.Now().Unix()

	return a.iUpgradePlan.ExecTransaction(ctx, func(ctx context.Context) error {
		err := a.iUpgradePlan.Update(ctx, *in.Id, dict)
		if err != nil {
			return err
		}

		upgradePlanCache.Remove(*in.Id)
		if emit {
			event.Emit(&event.Data{Type: model.UpgradePlanOpUpdate, Data: in})
		}

		return nil
	})
}

// Terminal
func (a *upgradePlan) Terminal(ctx context.Context, in *model.UpgradePlanTerminalRequest) error {

	var (
		dict = map[string]interface{}{
			"status":     model.UpgradePlanStatusCanceled,
			"updated_at": time.Now().Unix(),
		}
	)

	if err := a.check(ctx, in.Id, model.UpgradePlanOpCanceled, nil); err != nil {
		return err
	}

	return a.iUpgradePlan.ExecTransaction(ctx, func(ctx context.Context) error {
		if err := UserUpgrade.Terminal(ctx, &model.UserUpgradeTerminalRequest{PlanId: in.Id}); err != nil {
			return err
		}

		if err := a.iUpgradePlan.Update(ctx, in.Id, dict); err != nil {
			return err
		}

		event.Emit(&event.Data{Type: model.UpgradePlanOpCanceled, Data: &model.UpgradePlanDeleteRequest{Id: in.Id}})
		return nil
	})
}

// Delete
func (a *upgradePlan) Delete(ctx context.Context, in *model.UpgradePlanDeleteRequest) error {

	if err := a.check(ctx, in.Id, model.UpgradePlanOpDelete, nil); err != nil {
		return err
	}

	err := a.iUpgradePlan.Delete(ctx, in.Id)
	if err != nil {
		return err
	}

	/* 是否清空所有的升级结果? */
	event.Emit(&event.Data{Type: model.UpgradePlanOpDelete, Data: in})
	upgradePlanCache.Remove(in.Id)
	return a.iUserUpgrade.DeleteByPlanId(ctx, in.Id)
}

func (a *upgradePlan) ListUpgradeObject(ctx context.Context, in *model.ListUpgradeObjectRequest) (*model.UserUpgradeListResponse, error) {

	return a.userUpgrade.List(ctx, &model.UserUpgradeListRequest{PlanId: utils.GetValuePointer(in.Id)})
}

func (a *upgradePlan) ListUpgradeObject2(ctx context.Context, in *model.UpgradePlanListUpgradeObjectRequest) (*model.UpgradePlanListUpgradeObjectResponse, error) {

	if in.Index < 1 {
		return nil, fmt.Errorf("param index must start from 1")
	}

	if in.Size < 1 {
		return nil, fmt.Errorf("param index must start from 0")
	}

	var (
		err             error
		respUpgradePlan *model.UpgradePlanInfo
		qc              *model.OtaUpgradePlanQueryCond
	)

	_, respUpgradePlan, qc, err = a.getQueryCondAndDetail(ctx, in.Id)
	if err != nil {
		return nil, err
	}

	var (
		queryMap = map[model.TargetType]func(ctx context.Context, qc *model.OtaUpgradePlanQueryCond, in *model.UpgradePlanListUpgradeObjectRequest) (*model.UpgradePlanListUpgradeObjectResponse, error){
			model.TargetTypeEBikeTypes: a.ListUpgradeObjectByEBikeType,
			model.TargetTypeEBikeIds:   a.ListUpgradeObjectByEBike,
		}
	)

	if _, ok := queryMap[model.TargetType(respUpgradePlan.TargetType)]; !ok {
		return nil, fmt.Errorf("unsupport target type: %v", respUpgradePlan.TargetType)
	}

	return queryMap[model.TargetType(respUpgradePlan.TargetType)](ctx, qc, in)
}

func (a *upgradePlan) ListUpgradeObjectByEBikeType(ctx context.Context, qc *model.OtaUpgradePlanQueryCond, in *model.UpgradePlanListUpgradeObjectRequest) (*model.UpgradePlanListUpgradeObjectResponse, error) {

	eBikeTypeListRequest := &manager.GetEbikeTypeListReq{
		Index:       int32(in.Index),
		Size:        int32(in.Size),
		BikeTypeIds: qc.EBikeType,
	}
	ctx, _ = context.WithTimeout(ctx, time.Minute*5)

	if in.EbikeTypeName != nil && *in.EbikeTypeName != "" {
		eBikeTypeListRequest.BikeTypeName = *in.EbikeTypeName
	}

	if in.EbikeTypeName != nil && *in.EbikeTypeName != "" {
		eBikeTypeListRequest.BikeTypeName = *in.EbikeTypeName
	}

	if in.EbikeTypeId != nil && *in.EbikeTypeId != 0 {
		eBikeTypeListRequest.BikeTypeIds = []int64{*in.EbikeTypeId}
	}

	var (
		resp = &model.UpgradePlanListUpgradeObjectResponse{}
	)
	if bikeTypes, e := grpc.EBikeCmd.GetEbikeTypeList(ctx, eBikeTypeListRequest); e != nil {
		return nil, e
	} else {
		resp.Total = int(bikeTypes.Total)
		resp.List = bikeTypes.List
	}

	return resp, nil
}

func (a *upgradePlan) ListUpgradeObjectByEBike(ctx context.Context, qc *model.OtaUpgradePlanQueryCond, in *model.UpgradePlanListUpgradeObjectRequest) (*model.UpgradePlanListUpgradeObjectResponse, error) {

	eBikeListRequest := &manager.GetEbikeListReq{
		Index:   int32(in.Index),
		Size:    int32(in.Size),
		BikeIds: qc.EbikeId,
	}
	ctx, _ = context.WithTimeout(ctx, time.Minute*5)
	if in.EbikeTypeName != nil && *in.EbikeTypeName != "" {
		eBikeListRequest.BikeTypeName = *in.EbikeTypeName
	}

	if in.EbikeSnCode != nil && *in.EbikeSnCode != "" {
		eBikeListRequest.SnCode = *in.EbikeSnCode
	}

	if in.EbikeTypeId != nil && *in.EbikeTypeId != 0 {
		eBikeListRequest.BikeTypeIds = []int64{*in.EbikeTypeId}
	}

	var (
		resp = &model.UpgradePlanListUpgradeObjectResponse{}
	)
	if bikes, e := grpc.EBikeCmd.GetEbikeList(ctx, eBikeListRequest); e != nil {
		return nil, e
	} else {
		resp.Total = int(bikes.Total)
		resp.List = bikes.List
	}

	return resp, nil
}

// List
func (a *upgradePlan) List(ctx context.Context, in *model.UpgradePlanListRequest) (*model.UpgradePlanListResponse, error) {
	var (
		err          error
		total        int
		list         []*entity.UpgradePlan
		out          = &model.UpgradePlanListResponse{}
		idList       []int64
		planId2index = map[int64]int{}
		opts         = model.QueryOptions{OrderFields: model.NewOrderFields(model.NewOrderField("created_at", model.OrderByDESC))}
	)

	if total, list, err = a.iUpgradePlan.List(ctx, in, opts); err != nil {
		return nil, err
	} else {
		for i, e := range list {
			idList = append(idList, e.Id)
			planId2index[e.Id] = i
		}
	}

	out.Total = total
	out.List = mapping.UpgradePlansEntityToDto(list)

	if respStatistics, e := a.iUserUpgrade.Statistics(ctx, &model.UserUpgradeStatisticsRequest{PlanIds: idList}); e != nil {
		return nil, e
	} else {
		for _, v := range respStatistics.List {
			allTotal := v.SuccessTotal + v.FailedTotal + v.PrepareTotal + v.UpgradingTotal + v.CancelTotal
			out.List[planId2index[v.PlanId]].TotalCount = allTotal
			out.List[planId2index[v.PlanId]].SuccessCount = v.SuccessTotal
			out.List[planId2index[v.PlanId]].FailureCount = v.FailedTotal
			out.List[planId2index[v.PlanId]].PrepareCount = v.PrepareTotal
			out.List[planId2index[v.PlanId]].PrepareCount = v.UpgradingTotal
			out.List[planId2index[v.PlanId]].CancelCount = v.CancelTotal
			out.List[planId2index[v.PlanId]].CountDetail = fmt.Sprintf("%v/%v/%v/%v", v.FailedTotal, v.SuccessTotal, v.CancelTotal, allTotal)
		}
	}

	return out, nil
}

func (a *upgradePlan) updateStatus(ctx context.Context, in *model.UpgradePlanInfoRequest) error {

	/*
	   SELECT
	   plan_id,
	     CASE
	   WHEN COUNT(*) = COUNT(CASE WHEN status = 3 THEN 1 END) THEN 3
	   WHEN COUNT(CASE WHEN status = 4 THEN 1 END) > 0 THEN 4
	   ELSE MIN(status)  -- 可以添加其他逻辑处理其他状态
	   END AS status
	   FROM
	   user_upgrade
	   GROUP BY
	   plan_id;
	*/

	var (
		/*
		   1. upgrade_plan status 跟 user_upgrade status 的关系
		   2. upgrade_plan:status == 1 当 user_upgrade status的记录没有的时候表示待执行
		   3. upgrade_plan:status == 2 升级计划处于'升级中'的状态的时候,对应的升级进度的状态是:至少有一个进度处于待升级和升级中,并且没有取消升级的记录
		   4. upgrade_plan:status == 3 升级计划处于'已完成'的状态的时候,对应的升级进度的状态是:要么是处于'升级成功'的状态,要么是处于'升级失败'的状态,并且没有其他状态
		   5. upgrade_plan:status == 5 升级计划处于'取消升级'的状态的时候,对应的升级进度的状态是:至少有一个进度处于'取消升级'的状态,并且没有'待升级'的进度
		*/
		caseStatus = fmt.Sprintf(`
    CASE
        WHEN COUNT(*) = 0 THEN 1
        WHEN COUNT(CASE WHEN status = %v THEN 1 WHEN status = %v THEN 1 END) > 0 AND COUNT(CASE WHEN status = %v THEN 1  END) = 0 THEN %v
        WHEN COUNT(*) = COUNT(CASE WHEN status = %v THEN 1 WHEN status = %v THEN 1 END) THEN %v
        WHEN COUNT(CASE WHEN status = %v THEN 1 END) > 0 AND COUNT(CASE WHEN status = %v THEN 1 END) = 0 THEN %v
    END AS status`,
			int(model.UpgradeResultPending), int(model.UpgradeResultUpgrading), int(model.UpgradeResultAbolishment), int(model.UpgradePlanStatusUpgrading),
			int(model.UpgradeResultSuccess), int(model.UpgradeResultFailure), int(model.UpgradePlanStatusCompleted),
			int(model.UpgradeResultAbolishment), int(model.UpgradeResultPending), int(model.UpgradePlanStatusCanceled))
		selectFields = []string{
			"plan_id",
			caseStatus,
		}
	)
	_, resp, err := a.iUserUpgrade.List(ctx, &model.UserUpgradeListRequest{PlanId: &in.Id, Size: -1, Index: 1}, model.GetQueryOption(model.QueryOptions{
		OnlyList:     true,
		SelectFields: selectFields,
		GroupBy:      []string{"plan_id"},
	}))
	if err != nil {
		return err
	}

	var (
		dict = map[string]any{
			"updated_at": time.Now().Unix(),
		}
	)
	for _, v := range resp {
		dict["status"] = v.Status
		if err = a.iUpgradePlan.Update(ctx, in.Id, dict); err != nil {
			continue
		}
	}
	return nil
}

// Find
func (a *upgradePlan) Find(ctx context.Context, in *model.UpgradePlanInfoRequest) (*model.UpgradePlanInfo, error) {
	var (
		err  error
		data *entity.UpgradePlan
		out  = &model.UpgradePlanInfo{}
	)

	if data, err = a.iUpgradePlan.Find(ctx, in); err != nil {
		return nil, err
	}

	out = mapping.UpgradePlanEntityToDto(data)

	out.UpgradeStrategyInfo, _ = a.iUpgradeStrategy.Find(ctx, &model.UpgradeStrategyInfoRequest{Id: data.StrategyId})

	if respStatistics, e := a.iUserUpgrade.Statistics(ctx, &model.UserUpgradeStatisticsRequest{PlanId: in.Id}); e != nil {
		return nil, e
	} else {
		for _, v := range respStatistics.List {
			allTotal := v.SuccessTotal + v.FailedTotal + v.PrepareTotal + v.UpgradingTotal + v.CancelTotal
			out.TotalCount = allTotal
			out.SuccessCount = v.SuccessTotal
			out.FailureCount = v.FailedTotal
			out.PrepareCount = v.PrepareTotal
			out.PrepareCount = v.UpgradingTotal
			out.CancelCount = v.CancelTotal
			out.CountDetail = fmt.Sprintf("%v/%v/%v/%v", v.FailedTotal, v.SuccessTotal, v.CancelTotal, allTotal)
		}
	}

	return out, nil
}

func buildDetailData(ctx context.Context, strategyId int64) (*model.UpgradeStrategyInfo, json.RawMessage) {

	var (
		jsonDetail        = json.RawMessage{}
		upgradePlanDetail []*model.UpgradePlanDetail
	)
	respStrategy, err := UpgradeStrategy.Find(ctx, &model.UpgradeStrategyInfoRequest{
		Id: strategyId,
	})
	if err != nil {
		return respStrategy, jsonDetail
	}

	for _, v := range respStrategy.FirmwareIds {
		var data = &model.UpgradePlanDetail{}
		if resp, e := Firmware.FindDetail(ctx, &model.FirmwareInfoRequest{
			Id: v,
		}); e != nil {
			break
		} else {
			data.FirmwareId = v
			data.Version = resp.Version
			data.Name = resp.Name
			data.FilePath = resp.FilePath
			data.FileSize = resp.FileSize
			data.Description = resp.Description
			data.Type = resp.Types
			upgradePlanDetail = append(upgradePlanDetail, data)
		}
	}

	if len(upgradePlanDetail) > 0 {
		jsonDetail = func() json.RawMessage {
			b, _ := json.Marshal(upgradePlanDetail)
			return b
		}()
	}

	return respStrategy, jsonDetail
}

// buildUpgradePlan build entity
func buildUpgradePlan(ctx context.Context, in *model.UpgradePlanCreateRequest) *entity.UpgradePlan {

	ety := &entity.UpgradePlan{
		Detail: make(json.RawMessage, 0),
	}
	if v, ok := ctx.Value(auth.AuthUserKey).(int64); ok {
		ety.Operator = v
		// 车型名称校验

		var (
			operatorName = struct {
				name string
			}{}
		)
		postgres.GetDB(ctx).Table("users").Select("name").Where("id = ?", v).First(&operatorName)

		ety.OperatorName = operatorName.name
	}

	ety.Id = in.Id

	ety.StrategyId = in.StrategyId

	ety.Name = in.Name

	ety.TargetType = in.TargetType

	ety.QueryCond = in.QueryCond

	ety.StartTime = in.StartTime

	ety.Overseer = in.Overseer

	upStrategy, detail := buildDetailData(ctx, in.StrategyId)

	ety.Detail = detail

	ety.UpgradeType = upStrategy.UpgradeType

	ety.Tester = in.Tester

	ety.Inspector = in.Inspector

	ety.CreatedAt = time.Now().Unix()

	ety.UpdatedAt = in.UpdatedAt

	return ety
}
